﻿using System;
using System.Collections.Generic;
using System.Collections;

using SMLib.Automata.Base;
using SMLib.Base;
using SMLib.Automata.DFA;
using SMLib.Automata.MM;
using MBT.GraphAlgorithms;
using MBT.Utils;

namespace MBT.Conformance.OfflineTesting
{
	/// <summary>
	/// Offline Test case Generation [States Coverage] for DETERMINISTIC systems (Mealy machines and DFAs)
	/// </summary>
	public class OTCG_StateCoverage<STATETYPE, TRANSITIONTYPE>
	{
		protected IDiGraph<STATETYPE, TRANSITIONTYPE> testModel;
		protected List<Word> stateCoverSet;
		bool processed;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="testModel">the test model (deterministic Mealy machine or a DFA)</param>
		public OTCG_StateCoverage (IDiGraph<STATETYPE, TRANSITIONTYPE> testModel)
		{
			this.testModel = testModel;
			processed = false;
		}

		/// <summary>
		/// Explore the model BFS
		/// </summary>
		void BreathFirstSearch()
		{
			//* TODO					
		}
		
		/// <summary>
		/// Explore the model DFS
		/// </summary>
		void DepthFirstSearch()
		{
			//* TODO
		}
		
		/// <summary>
		/// Find the state coverage set for DETERMINISTIC case
		/// </summary>
		void FindStateCoverSet ()
		{
			//* init
			//* this can be used also for transition coverage test case generation!
			Tree<STATETYPE, TRANSITIONTYPE> testTree = new Tree<STATETYPE, TRANSITIONTYPE> (testModel [0]);
			List<Tree<STATETYPE, TRANSITIONTYPE>.TreeNode> treeFrontiers = new List<Tree<STATETYPE, TRANSITIONTYPE>.TreeNode> ();
			stateCoverSet = new List<Word> ();
			//* epsilon always belong to it!
			stateCoverSet.Add (new Word ());
			//* at the same time of generating tree, generate the coverage set
			List<Word> coverSetFrontier = new List<Word> ();
			List<short> nodeLevelInTree = new List<short> ();
			for (int i = 0; i < testModel.Size; i++)
				nodeLevelInTree.Add (short.MaxValue);			
			treeFrontiers.Add (testTree.Root);
			coverSetFrontier.Add (new Word ());
			
			short currentLevel = 0;
			while (treeFrontiers.Count > 0) {
				int numberOfFrontiers = treeFrontiers.Count;
				currentLevel++;
				//* expand all frontiers
				for (int i = 0; i < numberOfFrontiers; i++) {
					STATETYPE currenrState = treeFrontiers [0].NodeObject;
					Tree<STATETYPE, TRANSITIONTYPE>.TreeNode currentNodeInTree = treeFrontiers [0];
					treeFrontiers.RemoveAt (0);
					Word currentWord = coverSetFrontier [0];
					coverSetFrontier.RemoveAt (0);
					//* check if the state is already explored
					if (nodeLevelInTree [testModel.IndexOf(currentNodeInTree.NodeObject)] != short.MaxValue) {
						continue;
					}
					else
						nodeLevelInTree[testModel.IndexOf(currenrState)]=currentLevel;
					int iii = 0;
					foreach (TRANSITIONTYPE t in  testModel.TransitionsOf((currenrState))) {
						Word myWord;
						if (iii == 0)
							myWord = currentWord;
						else
							myWord = currentWord.Clone ();
						iii++;
						if (t is MealyMachine.Mealy_Transition) {
							//* the state is already visited
							//* This node should be considered as a leaf: dot't expand it.
							if (nodeLevelInTree [testModel.IndexOf(testModel.DestinationOf(t))] != short.MaxValue) {
								continue;
							}
							//MealyMachine.Mealy_State dst=(t as MealyMachine.Mealy_Transition).Destination;
							myWord.AddSymbol ((t as MealyMachine.Mealy_Transition).inLabel);
							Word w2=myWord.Clone();
							stateCoverSet.Add(w2);							
							//treeFrontiers.Add(dst);
							//coverSetFrontier.Add(w2);
						} else if (t is DFA.DFA_Transition) {
							myWord.AddSymbol ((t as DFA.DFA_Transition).Label);
						} else {
							throw new Exception ("State coverage is working only with DFAs and Mealy machines!");
						}
						Tree<STATETYPE, TRANSITIONTYPE>.TreeNode tempNode = currentNodeInTree.AddChild (testModel.DestinationOf (t), t);
						treeFrontiers.Add (tempNode);
						coverSetFrontier.Add (myWord);
					}
				}
			}
		}

		

		/// <summary>
		/// Preprocess
		/// </summary>
		public virtual void Process ()
		{
			if (processed)
				return;
			processed = true;
			FindStateCoverSet ();
		}

		/// <summary>
		/// Instead of Generating the whole test sequences, we better generate them one by one!
		/// </summary>
		/// <returns>enumarated test cases</returns>
		public virtual IEnumerable<Word> TestSequences ()
		{
			//* TODO integrate the main process with this method for all algos
			if (!processed)
				throw new Exception ("You should first use 'Process()'!");
			for (int i = 0; i < this.stateCoverSet.Count; i++) {
				yield return stateCoverSet[i];
			}
		}

		/// <summary>
		/// We can also generate the whole test suite (which is more memory/time consuming)
		/// </summary>
		/// <returns>the generated test suite</returns>
		public List<Word> GenerateTestSuite ()
		{
			if (!processed)
				throw new Exception ("You should first use 'Process()'!");
			List<Word> testSuite = new List<Word> ();
			foreach (Word testCase in TestSequences()) {
				testSuite.Add (testCase);
			}
			return testSuite;
		}      
	}
}
